package org.apache.rocketmq.store.logfile;


import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import org.apache.commons.lang3.SystemUtils;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageExtBatch;
import org.apache.rocketmq.common.message.MessageExtBrokerInner;
import org.apache.rocketmq.common.utils.NetworkUtil;
import org.apache.rocketmq.logging.org.slf4j.Logger;
import org.apache.rocketmq.logging.org.slf4j.LoggerFactory;
import org.apache.rocketmq.store.*;
import org.apache.rocketmq.store.config.FlushDiskType;
import org.apache.rocketmq.store.util.LibC;
import sun.misc.Unsafe;
import sun.nio.ch.DirectBuffer;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;


/**
 * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
 * @author：陈清风扬，个人微信号：chenqingfengyangjj。
 * @date:2025/6/29
 * @方法描述：内存映射文件对象
 */
public class DefaultMappedFile extends AbstractMappedFile {

    //操作系统缓存页大小，默认是4KB
    public static final int OS_PAGE_SIZE = 1024 * 4;

    //得到Unsafe对象，用于底层内存操作
    public static final Unsafe UNSAFE = getUnsafe();

    //判断内存映的数据是否被加载到内存的方法
    private static final Method IS_LOADED_METHOD;

    //实际使用的页内存大小，如果启用了Unsafe，则使用Unsafe的页内存大小，否则使用操作系统的页内存大小
    //UNSAFE.pageSize()得到的缓存页大小会跟随不同的操作系统和硬件平台进行设置
    public static final int UNSAFE_PAGE_SIZE = UNSAFE == null ? OS_PAGE_SIZE : UNSAFE.pageSize();

    protected static final Logger log = LoggerFactory.getLogger(LoggerName.STORE_LOGGER_NAME);

    //这是一个静态成员变量，所有的内存映射对象都会用到它
    //它是一个虚拟内存映射计数器对象，计算所有内存映射文件占用的虚拟内存总量
    //每当一个文件被映射时，计数器就会加fileSize
    protected static final AtomicLong TOTAL_MAPPED_VIRTUAL_MEMORY = new AtomicLong(0);

    //这是一个静态成员变量，所有的内存映射对象都会用到它
    //它是一个全局的内存映射计数器对象，每当一个文件被映射时，计数器就会加1
    protected static final AtomicInteger TOTAL_MAPPED_FILES = new AtomicInteger(0);

    //原子变量更新器，更新wrotePosition成员变量
    protected static final AtomicIntegerFieldUpdater<DefaultMappedFile> WROTE_POSITION_UPDATER;
    //更新committedPosition成员变量
    protected static final AtomicIntegerFieldUpdater<DefaultMappedFile> COMMITTED_POSITION_UPDATER;
    //更新wrotePosition
    protected static final AtomicIntegerFieldUpdater<DefaultMappedFile> FLUSHED_POSITION_UPDATER;

    //消息追加到内存映射缓冲区的位置
    protected volatile int wrotePosition;
    //提交到操作系统缓存页的消息位置
    protected volatile int committedPosition;
    //已经刷新到本地硬盘上的消息的位置
    protected volatile int flushedPosition;
    //当个内存映射文件的大小
    protected int fileSize;
    //内存映射文件的通道
    protected FileChannel fileChannel;
    //写入缓冲区，这个缓冲区就是临时存储器分配过来的
    //只有启用了临时存储池，这个成员变量才会被复制
    protected ByteBuffer writeBuffer = null;
    //临时存储池
    protected TransientStorePool transientStorePool = null;
    //文件名称
    protected String fileName;
    //当前内存映射文件的起始偏移，这个起始偏移量就是该文件在CommitLog文件中的全局偏移量
    protected long fileFromOffset;
    //文件对象
    protected File file;
    //内存映射缓冲区
    protected MappedByteBuffer mappedByteBuffer;
    //最后消息的时间
    protected volatile long storeTimestamp = 0;
    //当前内存映射文件是否是文件队列中的第一个文件
    protected boolean firstCreateInQueue = false;
    //消息最后的刷盘时间
    private long lastFlushTime = -1L;
    //待清理的旧内存映射缓冲区
    protected MappedByteBuffer mappedByteBufferWaitToClean = null;
    //最后一次内存交换时间
    protected long swapMapTime = 0L;
    //自上次内存交换后的访问计数
    protected long mappedByteBufferAccessCountSinceLastSwap = 0L;

    //对于消费队列内存映射文件而言，这个成员变量就是当前内存映射文件存储第一条消息时的时间
    //因为在rocketmq中，ConsumeQueue消费队列内存映射文件使用的也是MappedFile对象，所以在当前类中，有一些成员变量时为消费队列定义的
    private long startTimestamp = -1;
    //对于消费队列内存映射文件而言，这个成员变量就是当前内存映射文件存储最后一条消息时的时间
    private long stopTimestamp = -1;

    //静态代码块
    static {
        //创建wrotePosition成员变量的源自更新器
        WROTE_POSITION_UPDATER = AtomicIntegerFieldUpdater.newUpdater(DefaultMappedFile.class, "wrotePosition");
        //创建committedPosition成员变量的更新器
        COMMITTED_POSITION_UPDATER = AtomicIntegerFieldUpdater.newUpdater(DefaultMappedFile.class, "committedPosition");
        //创建flushedPosition成员变量的更新器
        FLUSHED_POSITION_UPDATER = AtomicIntegerFieldUpdater.newUpdater(DefaultMappedFile.class, "flushedPosition");
        Method isLoaded0method = null;
        if (!SystemUtils.IS_OS_WINDOWS) {
            try {
                //反射获取MappedByteBuffer类的isLoaded0私有方法，该方法会检测内存映射数据是否加载到内存
                isLoaded0method = MappedByteBuffer.class.getDeclaredMethod("isLoaded0", long.class, long.class, int.class);
                isLoaded0method.setAccessible(true);
            } catch (NoSuchMethodException ignore) {

            }
        }//将方法赋值给IS_LOADED_METHOD静态变量
        IS_LOADED_METHOD = isLoaded0method;
    }

    //构造方法
    public DefaultMappedFile() {
    }

    //构造方法
    public DefaultMappedFile(final String fileName, final int fileSize) throws IOException {
        init(fileName, fileSize);
    }

    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：使用临时存储池的构造方法
     */
    public DefaultMappedFile(final String fileName, final int fileSize,
                             final TransientStorePool transientStorePool) throws IOException {

        init(fileName, fileSize);
        //在这里从临时存储池中得到了一个堆外内存缓冲区，赋值给writeBuffer成员变量
        this.writeBuffer = transientStorePool.borrowBuffer();
        this.transientStorePool = transientStorePool;
    }


    public static int getTotalMappedFiles() {
        return TOTAL_MAPPED_FILES.get();
    }


    public static long getTotalMappedVirtualMemory() {
        return TOTAL_MAPPED_VIRTUAL_MEMORY.get();
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：初始化内存映射文件对象的方法
     */
    @Override
    public void init(final String fileName, final int fileSize, final TransientStorePool transientStorePool) throws IOException {
        init(fileName, fileSize);
        this.writeBuffer = transientStorePool.borrowBuffer();
        this.transientStorePool = transientStorePool;
    }

    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：初始化内存映射文件对象的方法
     */
    private void init(final String fileName, final int fileSize) throws IOException {
        //设置文件名称
        this.fileName = fileName;
        //设置文件大小
        this.fileSize = fileSize;
        //创建文件对象
        this.file = new File(fileName);
        //因为rocketmq规定的内存映射文件的名称就是该文件的起始偏移量
        //所以这里直接解析名称就可以得到当前内存映射文件的起始偏移
        this.fileFromOffset = Long.parseLong(this.file.getName());
        //定义一个初始化操作是否成功的标志
        boolean ok = false;
        //确保父目录存在
        UtilAll.ensureDirOK(this.file.getParent());
        try {
            //打开文件通道，获取文件通道对象
            this.fileChannel = new RandomAccessFile(this.file, "rw").getChannel();
            //创建内存映射缓冲区，映射范围为0——fileSize
            this.mappedByteBuffer = this.fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);
            //增加虚拟内存映总量
            TOTAL_MAPPED_VIRTUAL_MEMORY.addAndGet(fileSize);
            //增加内存映射文件计数
            TOTAL_MAPPED_FILES.incrementAndGet();
            //设置初始化成功标志
            ok = true;
        } catch (FileNotFoundException e) {
            log.error("Failed to create file " + this.fileName, e);
            throw e;
        } catch (IOException e) {
            log.error("Failed to map file " + this.fileName, e);
            throw e;
        } finally {
            //初始化操作失败则释放资源
            if (!ok && this.fileChannel != null) {
                this.fileChannel.close();
            }
        }
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：重命名内存映射文件的方法
     */
    @Override
    public boolean renameTo(String fileName) {
        //创建新的文件对象
        File newFile = new File(fileName);
        //执行文件重命名
        boolean rename = file.renameTo(newFile);
        if (rename) {
            //操作成功则更新文件名属性
            this.fileName = fileName;
            this.file = newFile;
        }
        return rename;
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：得到文件最后修改时间的方法
     */
    @Override
    public long getLastModifiedTimestamp() {
        return this.file.lastModified();
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：根据指定偏移量，以及读取长度，从指定位置读取数据到ByteBuffer中
     */
    public boolean getData(int pos, int size, ByteBuffer byteBuffer) {
        //检查目标缓冲区剩余空间是否够用
        if (byteBuffer.remaining() < size) {
            return false;
        }
        //得到当前内存映射文件已追加的消息的最大偏移量
        int readPosition = getReadPosition();
        //校验要读取的数据的有效性，只有小于内存映射文件最大数据偏移量才能读取
        if ((pos + size) <= readPosition) {
            //增加当前文件使用计数
            if (this.hold()) {
                try {
                    //在这里读取数据到目标缓冲区中
                    int readNum = fileChannel.read(byteBuffer, pos);
                    //判断最终读取的字节是否是用户希望的
                    return size == readNum;
                } catch (Throwable t) {
                    log.warn("Get data failed pos:{} size:{} fileFromOffset:{}", pos, size, this.fileFromOffset);
                    return false;
                } finally {
                    //在这里减少文件使用计数
                    this.release();
                }
            } else {
                log.debug("matched, but hold failed, request pos: " + pos + ", fileFromOffset: " + this.fileFromOffset);
            }
        } else {
            log.warn("selectMappedBuffer request pos invalid, request pos: " + pos + ", size: " + size + ", fileFromOffset: " + this.fileFromOffset);
        }
        return false;
    }


    @Override
    public int getFileSize() {
        return fileSize;
    }


    @Override
    public FileChannel getFileChannel() {
        return fileChannel;
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：追加消息到内存映射文件缓冲区的方法，该方法在第十四版本代码中并没有被用到，我就不添加注释了
     */
    public AppendMessageResult appendMessage(final ByteBuffer byteBufferMsg, final CompactionAppendMsgCallback cb) {
        assert byteBufferMsg != null;
        assert cb != null;
        int currentPos = WROTE_POSITION_UPDATER.get(this);
        if (currentPos < this.fileSize) {
            ByteBuffer byteBuffer = appendMessageBuffer().slice();
            byteBuffer.position(currentPos);
            AppendMessageResult result = cb.doAppend(
                    byteBuffer,
                    this.fileFromOffset,
                    this.fileSize - currentPos,
                    byteBufferMsg
            );
            WROTE_POSITION_UPDATER.addAndGet(this, result.getWroteBytes());
            this.storeTimestamp = result.getStoreTimestamp();
            return result;
        }
        log.error("MappedFile.appendMessage return null, wrotePosition: {} fileSize: {}", currentPos, this.fileSize);
        return new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR);
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：追加单挑消息到内存映射文件缓冲区的方法，在第十四版本代码中，调用的是这个方法
     */
    @Override
    public AppendMessageResult appendMessage(final MessageExtBrokerInner msg, final AppendMessageCallback cb,
                                             PutMessageContext putMessageContext) {
        return appendMessagesInner(msg, cb, putMessageContext);
    }

    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：追加批量消息到内存映射文件缓冲区的方法
     */
    @Override
    public AppendMessageResult appendMessages(final MessageExtBatch messageExtBatch, final AppendMessageCallback cb,
                                              PutMessageContext putMessageContext) {
        return appendMessagesInner(messageExtBatch, cb, putMessageContext);
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：真正追加消息到内存映射文件缓冲区的方法
     */
    public AppendMessageResult appendMessagesInner(final MessageExt messageExt, final AppendMessageCallback cb,
                                                   PutMessageContext putMessageContext) {
        //对消息对象进行判空校验
        assert messageExt != null;
        //对回调对象进行判空校验
        assert cb != null;
        //得到内存映射文件当前的写入量
        int currentPos = WROTE_POSITION_UPDATER.get(this);
        //判断是否还有剩余空间
        if (currentPos < this.fileSize) {
            //如果还有剩余空间，那接下来就执行具体的追加消息操作
            //这里得到内存映射文件的缓冲区对象，注意，这个appendMessageBuffer方法得到的缓冲区可能是内存映射文件的映射缓冲区
            //也可能是临时存储池分配的缓冲区，要是启用了临时存储池功能，这里得到的就是临时存储池分配的缓冲区对象
            ByteBuffer byteBuffer = appendMessageBuffer().slice();
            //设置缓冲区写入数据的起始指针
            byteBuffer.position(currentPos);
            AppendMessageResult result;
            //这里执行的是批量消息追加的操作
            if (messageExt instanceof MessageExtBatch && !((MessageExtBatch) messageExt).isInnerBatch()) {
                result = cb.doAppend(
                        this.getFileFromOffset(),
                        byteBuffer,
                        this.fileSize - currentPos,
                        (MessageExtBatch) messageExt,
                        putMessageContext
                );
            }
            //这里执行的是单挑消息追加的操作
            else if (messageExt instanceof MessageExtBrokerInner) {
                result = cb.doAppend(
                        //传入内存映射文件起始偏移
                        this.getFileFromOffset(),
                        //刚才得到的要写入数据的缓冲区
                        byteBuffer,
                        //当前内存映射缓冲区剩余的可用空间
                        this.fileSize - currentPos,
                        //转换消息类型
                        (MessageExtBrokerInner) messageExt,
                        //传出消息存储上下文信息
                        putMessageContext
                );
            }
            else {//未知消息类型则返回错误结果
                return new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR);
            }
            //追加完毕之后，使用原子更新消息写入位置
            WROTE_POSITION_UPDATER.addAndGet(this, result.getWroteBytes());
            //更新最新的消息存储时间
            this.storeTimestamp = result.getStoreTimestamp();
            return result;
        }
        //缓冲区空间不足则返回错误结果
        log.error("MappedFile.appendMessage return null, wrotePosition: {} fileSize: {}", currentPos, this.fileSize);
        return new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR);
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：得到可追加消息的内存映射缓冲区
     */
    protected ByteBuffer appendMessageBuffer() {
        //递增缓冲区访问次数
        this.mappedByteBufferAccessCountSinceLastSwap++;
        //如果启用了临时存储池，那这里得到的就是临时存储池分配的堆外内存缓冲区，否则就是内存映射缓冲区
        return writeBuffer != null ? writeBuffer : this.mappedByteBuffer;
    }


    @Override
    public long getFileFromOffset() {
        return this.fileFromOffset;
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：追加字节数组到内存映射文件中的方法
     */
    @Override
    public boolean appendMessage(final byte[] data) {
        return appendMessage(data, 0, data.length);
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：追加ByteBuffer格式的消息到操作系统缓存页的方法
     */
    @Override
    public boolean appendMessage(ByteBuffer data) {
        int currentPos = WROTE_POSITION_UPDATER.get(this);
        int remaining = data.remaining();
        if ((currentPos + remaining) <= this.fileSize) {
            try {
                this.fileChannel.position(currentPos);
                while (data.hasRemaining()) {
                    //判断是否还有数据要写
                    //如果有就直接把数据写入到操作系统缓存页，注意，这里就和内存映射缓冲区没关系了
                    //大家要注意区分
                    this.fileChannel.write(data);
                }
            } catch (Throwable e) {
                log.error("Error occurred when append message to mappedFile.", e);
            }
            WROTE_POSITION_UPDATER.addAndGet(this, remaining);
            return true;
        }
        return false;
    }

    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：追加字节数组到内存映射文件中的方法
     */
    @Override
    public boolean appendMessage(final byte[] data, final int offset, final int length) {
        int currentPos = WROTE_POSITION_UPDATER.get(this);
        if ((currentPos + length) <= this.fileSize) {
            try {
                ByteBuffer buf = this.mappedByteBuffer.slice();
                buf.position(currentPos);
                //在这里把数据放到mappedByteBuffer中
                buf.put(data, offset, length);
            } catch (Throwable e) {
                log.error("Error occurred when append message to mappedFile.", e);
            }
            WROTE_POSITION_UPDATER.addAndGet(this, length);
            return true;
        }
        return false;
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：把操作系统缓存页中的消息刷新到本地硬盘的方法
     */
    @Override
    public int flush(final int flushLeastPages) {
        //判断本次操作是否满足刷盘条件，其实就是判断要刷新到本地的数据的大小是否超过了指定的缓存页容量
        if (this.isAbleToFlush(flushLeastPages)) {
            //增加内存文件使用计数
            if (this.hold()) {
                //得到当前已写入的消息位置
                int value = getReadPosition();
                try {
                    //增加内存文件使用计数
                    this.mappedByteBufferAccessCountSinceLastSwap++;
                    //判断是否使用了临时存储池的堆外内存，如果使用了则直接使用文件通道刷盘
                    if (writeBuffer != null || this.fileChannel.position() != 0) {
                        //在这里强制把缓存页中的数据刷新到硬盘
                        this.fileChannel.force(false);
                    } else {
                        //如果没有使用临时存储池，那这里就是使用内存映射缓冲区刷盘
                        //这里我要给大家解释一下，内存映射操作执行之后，读取数据的时候，肯定会从内存映射缓冲区中读取数据
                        //在这种情况下，如果写入数据也使用内存映射缓冲区，使用的都是相同的缓存页，那么读写同时进行的时候，也许会出现并发问题
                        //这个时候就需要使用缓存页的锁来保证数据一致性，当然，这肯定也会对程序性能带来一些影响
                        //在这种情况下，如果使用了临时存储池，临时存储池使用的是堆外内存缓冲区，写入数据操作不会经过文件映射缓冲区
                        //这样一来写入数据使用堆外内存，读取数据使用内存映射缓冲区，就实现了内存级别的读写分离
                        this.mappedByteBuffer.force();
                    }
                    //得到最后刷新的时间
                    this.lastFlushTime = System.currentTimeMillis();
                } catch (Throwable e) {
                    log.error("Error occurred when force data to disk.", e);
                }

                //更新最新的刷盘位置
                FLUSHED_POSITION_UPDATER.set(this, value);
                //释放资源锁
                this.release();
            } else {//对内存文件引用失败
                log.warn("in flush, hold failed, flush offset = " + FLUSHED_POSITION_UPDATER.get(this));
                //则更新最后刷盘位置为当前写入消息位置
                FLUSHED_POSITION_UPDATER.set(this, getReadPosition());
            }
        }
        //返回当前已刷新的消息位置
        return this.getFlushedPosition();
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：提交数据到操作系统缓存页的方法
     */
    @Override
    public int commit(final int commitLeastPages) {
        //判断是否使用了临时存储池的堆外内存，如果没有使用，则意味着没有要提交的数据
        if (writeBuffer == null) {
            //这个时候直接返回消息写入位置即可
            return WROTE_POSITION_UPDATER.get(this);
        }
        //如果使用了临时存储池，但是并不真的提交数据
        if (transientStorePool != null && !transientStorePool.isRealCommit()) {
            //在这里设置已提交位置为消息写入位置
            COMMITTED_POSITION_UPDATER.set(this, WROTE_POSITION_UPDATER.get(this));
        }
        //检查是否符合提交数据的条件
        else if (this.isAbleToCommit(commitLeastPages)) {
            if (this.hold()) {
                //在这里执行真正提交数据的操作
                commit0();
                this.release();
            } else {
                log.warn("in commit, hold failed, commit offset = " + COMMITTED_POSITION_UPDATER.get(this));
            }
        }
        //在这里判断一下已提交的数据是否和内存映射文件的大小一样了
        //如果一样就意味着这个内存映射文件对应的本地文件已经写满了，这个时候就可以把临时存储池分配的堆外缓冲区归还给临时存储池了
        //让下一个内存映射文件复用
        if (writeBuffer != null && this.transientStorePool != null && this.fileSize == COMMITTED_POSITION_UPDATER.get(this)) {
            //归还缓冲区到临时存储池
            this.transientStorePool.returnBuffer(writeBuffer);
            //清空缓冲区引用
            this.writeBuffer = null;
        }
        //返回最新的消息提交位置
        return COMMITTED_POSITION_UPDATER.get(this);
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：真正提交消息的方法
     */
    protected void commit0() {
        //得到当前消息的写入位置
        int writePos = WROTE_POSITION_UPDATER.get(this);
        //得到消息已经提交的位置
        int lastCommittedPosition = COMMITTED_POSITION_UPDATER.get(this);
        //判断是否存在未提交的消息数据
        if (writePos - lastCommittedPosition > 0) {
            try {
                //如果存在则执行消息提交操作
                //得到临时存储池分配的缓冲区中的消息
                ByteBuffer byteBuffer = writeBuffer.slice();
                byteBuffer.position(lastCommittedPosition);
                byteBuffer.limit(writePos);
                //在这里设置文件的写入位置
                this.fileChannel.position(lastCommittedPosition);
                //在这里把消息写入到文件通道中，存储到操作系统缓存中
                this.fileChannel.write(byteBuffer);
                //更新消息已提交位置
                COMMITTED_POSITION_UPDATER.set(this, writePos);
            } catch (Throwable e) {
                log.error("Error occurred when commit data to FileChannel.", e);
            }
        }
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：判断是否满足刷盘条件的方法
     */
    private boolean isAbleToFlush(final int flushLeastPages) {
        int flush = FLUSHED_POSITION_UPDATER.get(this);
        int write = getReadPosition();
        //如果当前内存映射文件对应的本地文件已经写满了，就直接返回
        if (this.isFull()) {
            return true;
        }
        //计算是否符合最小刷盘页数
        if (flushLeastPages > 0) {
            return ((write / OS_PAGE_SIZE) - (flush / OS_PAGE_SIZE)) >= flushLeastPages;
        }
        //这种情况肯定也意味着存在刷盘数据，那页返回true
        //等于的话就会返回false了
        return write > flush;
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：判断是否满足提交条件的方法
     */
    protected boolean isAbleToCommit(final int commitLeastPages) {
        int commit = COMMITTED_POSITION_UPDATER.get(this);
        int write = WROTE_POSITION_UPDATER.get(this);
        if (this.isFull()) {
            return true;
        }
        if (commitLeastPages > 0) {
            return ((write / OS_PAGE_SIZE) - (commit / OS_PAGE_SIZE)) >= commitLeastPages;
        }
        return write > commit;
    }


    @Override
    public int getFlushedPosition() {
        return FLUSHED_POSITION_UPDATER.get(this);
    }


    @Override
    public void setFlushedPosition(int pos) {
        FLUSHED_POSITION_UPDATER.set(this, pos);
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：判断当前文件是否已写满的方法
     */
    @Override
    public boolean isFull() {
        return this.fileSize == WROTE_POSITION_UPDATER.get(this);
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：得的到指定位置的内存映射缓冲区的消息内容
     */
    @Override
    public SelectMappedBufferResult selectMappedBuffer(int pos, int size) {
        //得到消息已经写入的位置
        int readPosition = getReadPosition();
        //判断要读取的消息的范围是否超过了最大可读位置
        if ((pos + size) <= readPosition) {
            if (this.hold()) {
                //递增内存访问计数器
                this.mappedByteBufferAccessCountSinceLastSwap++;
                //创建共享内存缓冲区，这里创建的是整个内存映射缓冲区视图
                ByteBuffer byteBuffer = this.mappedByteBuffer.slice();
                //设置读取消息的起始位置
                byteBuffer.position(pos);
                //这里创建的是限制了起始位置的缓冲区视图
                ByteBuffer byteBufferNew = byteBuffer.slice();
                //设置消息的读取长度
                byteBufferNew.limit(size);
                //返回得到的消息结果
                return new SelectMappedBufferResult(
                        //消息的全局偏移量
                        this.fileFromOffset + pos,
                        //封装了消息内容的只读数据缓冲区
                        byteBufferNew,
                        //数据长度
                        size,
                        //资源持有者，用于后续释放缓冲区
                        this
                );
            } else {
                log.warn("matched, but hold failed, request pos: " + pos + ", fileFromOffset: " + this.fileFromOffset);
            }
        } else {
            log.warn("selectMappedBuffer request pos invalid, request pos: " + pos
                    + ", size: " + size
                    + ", fileFromOffset: " + this.fileFromOffset);
        }
        return null;
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：根据指定的起始位置得到直到末尾的消息得缓冲区视图对象，该方法和上面方法得逻辑几乎相同，我就不添加注释了，大家自己看看就行
     */
    @Override
    public SelectMappedBufferResult selectMappedBuffer(int pos) {
        int readPosition = getReadPosition();
        if (pos < readPosition && pos >= 0) {
            if (this.hold()) {
                this.mappedByteBufferAccessCountSinceLastSwap++;
                ByteBuffer byteBuffer = this.mappedByteBuffer.slice();
                byteBuffer.position(pos);
                int size = readPosition - pos;
                ByteBuffer byteBufferNew = byteBuffer.slice();
                byteBufferNew.limit(size);
                return new SelectMappedBufferResult(
                        this.fileFromOffset + pos,
                        byteBufferNew,
                        size,
                        this
                );
            }
        }
        return null;
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：清理内存映射资源的方法
     */
    @Override
    public boolean cleanup(final long currentRef) {
        //判断当前内存映射文件是否仍被引用
        if (this.isAvailable()) {
            log.error("this file[REF:" + currentRef + "] " + this.fileName
                    + " have not shutdown, stop unmapping.");
            //如果仍然被引用，那就返回false，表示不需要清理
            return false;
        }
        //判断当前内存映射文件是否已完成清理
        if (this.isCleanupOver()) {
            log.error("this file[REF:" + currentRef + "] " + this.fileName
                    + " have cleanup, do not do it again.");
            //如果已完成清理，那就返回true，表示不需要清理
            return true;
        }

        //接下来就是实际的清理操作
        //清理映射缓冲区
        UtilAll.cleanBuffer(this.mappedByteBuffer);
        //清理待清理缓冲区
        UtilAll.cleanBuffer(this.mappedByteBufferWaitToClean);
        //引用计数置空
        this.mappedByteBufferWaitToClean = null;
        //减少虚拟内存计数
        TOTAL_MAPPED_VIRTUAL_MEMORY.addAndGet(this.fileSize * (-1));
        //减少内存映射文件计数
        TOTAL_MAPPED_FILES.decrementAndGet();
        log.info("unmap file[REF:" + currentRef + "] " + this.fileName + " OK");
        return true;
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：销毁内存映射文件的方法
     */
    @Override
    public boolean destroy(final long intervalForcibly) {
        //执行关闭资源的方法
        this.shutdown(intervalForcibly);
        //判断是否已经清理完毕了，清理完毕了才能执行释放文件资源的操作
        if (this.isCleanupOver()) {
            try {
                //的到最后修改内存映射文件的时间
                long lastModified = getLastModifiedTimestamp();
                //关闭文件通道
                this.fileChannel.close();
                log.info("close file channel " + this.fileName + " OK");
                long beginTime = System.currentTimeMillis();
                //删除硬盘上的文件
                boolean result = this.file.delete();
                //记录操作结果
                log.info("delete file[REF:" + this.getRefCount() + "] " + this.fileName
                        + (result ? " OK, " : " Failed, ")
                        + "W:" + this.getWrotePosition()
                        + " M:" + this.getFlushedPosition() + ", "
                        + UtilAll.computeElapsedTimeMilliseconds(beginTime) + ","
                        + (System.currentTimeMillis() - lastModified));

                return result;
            } catch (Exception e) {
                log.warn("close file channel " + this.fileName + " Failed. ", e);
            }
            return true;
        } else {
            log.warn("destroy mapped file[REF:" + this.getRefCount() + "] " + this.fileName
                    + " Failed. cleanupOver: " + this.cleanupOver);
        }
        return false;
    }



    @Override
    public int getWrotePosition() {
        return WROTE_POSITION_UPDATER.get(this);
    }


    @Override
    public void setWrotePosition(int pos) {
        WROTE_POSITION_UPDATER.set(this, pos);
    }

    //得到消息已经写入的位置，如果没有使用临时存储池，那么得到的就是消息写入内存映射缓冲区的位置
    //如果使用了临时存储池，那么得到的就是已提交的消息的位置
    @Override
    public int getReadPosition() {
        return transientStorePool == null || !transientStorePool.isRealCommit()
                ? WROTE_POSITION_UPDATER.get(this)
                : COMMITTED_POSITION_UPDATER.get(this);
    }


    @Override
    public void setCommittedPosition(int pos) {
        COMMITTED_POSITION_UPDATER.set(this, pos);
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：对内存映射文件进行预热的方法，至于需要预热的原因，我已经在AllocateMappedFileService类的mmapOperation()方法中解释过了
     */
    @Override
    public void warmMappedFile(FlushDiskType type, int pages) {
        //增加内存访问计数，避免数据被swap机制换出到硬盘上
        this.mappedByteBufferAccessCountSinceLastSwap++;
        //记录预热操作开始执行的时间
        long beginTime = System.currentTimeMillis();
        //创建内存映射缓冲区的切片，以便共享底层数据
        ByteBuffer byteBuffer = this.mappedByteBuffer.slice();
        //记录刷盘位置
        long flush = 0;
        //逐页遍历文件，每一页大小为4KB
        for (long i = 0, j = 0; i < this.fileSize; i += DefaultMappedFile.OS_PAGE_SIZE, j++) {
            //向文件映射的每一页都写入一个0字节，以便触发缺页中断
            //注意，上面循环的条件i += DefaultMappedFile.OS_PAGE_SIZE，表示每一次循环i就会增加一个缓存页的大小
            byteBuffer.put((int) i, (byte) 0);
            //如果当前配置的是同步刷盘操作，这个时候就会把内存映射缓冲区的数据刷新到本地硬盘上
            if (type == FlushDiskType.SYNC_FLUSH) {
                //判断是否达到了可以刷盘的阈值
                if ((i / OS_PAGE_SIZE) - (flush / OS_PAGE_SIZE) >= pages) {
                    flush = i;
                    //达到的话则把数据刷盘，首次写入数据，避免后续写入数据时有IO延迟
                    mappedByteBuffer.force();
                }
            }
        }
        if (type == FlushDiskType.SYNC_FLUSH) {
            log.info("mapped file warm-up done, force to disk, mappedFile={}, costTime={}", this.getFileName(), System.currentTimeMillis() - beginTime);
            mappedByteBuffer.force();
        }
        //记录预热完成日志信息
        log.info("mapped file warm-up done. mappedFile={}, costTime={}", this.getFileName(), System.currentTimeMillis() - beginTime);
        //将当前内存上锁，防止数据被交换到硬盘
        this.mlock();
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：内存交换的方法，该方法会释放旧映射缓冲区并创建新映射缓冲区以回收资源。
     */
    @Override
    public boolean swapMap() {
        //当前内存映射文件的引用计数为1，并且没有带清理的旧映射缓冲区，这个时候就可以执行交换操作了
        if (getRefCount() == 1 && this.mappedByteBufferWaitToClean == null) {
            //获取文件引用
            if (!hold()) {
                log.warn("in swapMap, hold failed, fileName: " + this.fileName);
                return false;
            }
            try {
                //得到旧的映射缓冲区
                this.mappedByteBufferWaitToClean = this.mappedByteBuffer;
                //创建新的映射缓冲区
                this.mappedByteBuffer = this.fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);
                //重置访问计数器
                this.mappedByteBufferAccessCountSinceLastSwap = 0L;
                //记录最新的内存交换时间
                this.swapMapTime = System.currentTimeMillis();
                log.info("swap file " + this.fileName + " success.");
                return true;
            } catch (Exception e) {
                log.error("swapMap file " + this.fileName + " Failed. ", e);
            } finally {
                //释放引用资源
                this.release();
            }
        } else {
            log.info("Will not swap file: " + this.fileName + ", ref=" + getRefCount());
        }
        return false;
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：清理旧的内存映射缓冲区的方法
     */
    @Override
    public void cleanSwapedMap(boolean force) {
        try {
            if (this.mappedByteBufferWaitToClean == null) {
                return;
            }
            //设置最小清理间隔时间
            long minGapTime = 120 * 1000L;
            //计算距离上次交换的时间差
            long gapTime = System.currentTimeMillis() - this.swapMapTime;
            //如果是非强制模式并且距离上次交换的时间差小于最小清理间隔时间，则等待一会
            if (!force && gapTime < minGapTime) {
                Thread.sleep(minGapTime - gapTime);
            }
            //执行清理操作
            UtilAll.cleanBuffer(this.mappedByteBufferWaitToClean);
            //引用置空
            mappedByteBufferWaitToClean = null;
            log.info("cleanSwapedMap file " + this.fileName + " success.");
        } catch (Exception e) {
            log.error("cleanSwapedMap file " + this.fileName + " Failed. ", e);
        }
    }


    @Override
    public long getRecentSwapMapTime() {
        return 0;
    }


    @Override
    public long getMappedByteBufferAccessCountSinceLastSwap() {
        return this.mappedByteBufferAccessCountSinceLastSwap;
    }


    @Override
    public long getLastFlushTime() {
        return this.lastFlushTime;
    }


    @Override
    public String getFileName() {
        return fileName;
    }


    @Override
    public MappedByteBuffer getMappedByteBuffer() {
        this.mappedByteBufferAccessCountSinceLastSwap++;
        return mappedByteBuffer;
    }


    @Override
    public ByteBuffer sliceByteBuffer() {
        this.mappedByteBufferAccessCountSinceLastSwap++;
        return this.mappedByteBuffer.slice();
    }



    @Override
    public long getStoreTimestamp() {
        return storeTimestamp;
    }


    @Override
    public boolean isFirstCreateInQueue() {
        return firstCreateInQueue;
    }


    @Override
    public void setFirstCreateInQueue(boolean firstCreateInQueue) {
        this.firstCreateInQueue = firstCreateInQueue;
    }



    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：给内存映射区域上锁的方法，防止这部分内存被交换到硬盘
     */
    @Override
    public void mlock() {
        final long beginTime = System.currentTimeMillis();
        //得到内存映射缓冲区的底层内存地址
        final long address = ((DirectBuffer) (this.mappedByteBuffer)).address();
        //创建指向该地址的指针
        Pointer pointer = new Pointer(address);
        {   //锁定内存区域
            int ret = LibC.INSTANCE.mlock(pointer, new NativeLong(this.fileSize));
            log.info("mlock {} {} {} ret = {} time consuming = {}",
                    address, this.fileName, this.fileSize, ret,
                    System.currentTimeMillis() - beginTime);
        }
        {   //表示这块内存很快就会被访问，操作系统会尽可能将映射的硬盘文件的数据加载到这块内存
            int ret = LibC.INSTANCE.madvise(pointer, new NativeLong(this.fileSize), LibC.MADV_WILLNEED);
            log.info("madvise {} {} {} ret = {} time consuming = {}",
                    address, this.fileName, this.fileSize, ret,
                    System.currentTimeMillis() - beginTime);
        }
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：为内存区域解除锁定的方法
     */
    @Override
    public void munlock() {
        final long beginTime = System.currentTimeMillis();
        final long address = ((DirectBuffer) (this.mappedByteBuffer)).address();
        Pointer pointer = new Pointer(address);
        //解除内存锁定
        int ret = LibC.INSTANCE.munlock(pointer, new NativeLong(this.fileSize));
        log.info("munlock {} {} {} ret = {} time consuming = {}",
                address, this.fileName, this.fileSize, ret,
                System.currentTimeMillis() - beginTime);
    }



    @Override
    public File getFile() {
        return this.file;
    }


    //以下两个方法在第十四版本代码中没有用到，我就先不添加注释了，否则要写的注释就太多了，实在是写不过来了，请大家谅解一下

    @Override
    public void renameToDelete() {
        if (!fileName.endsWith(".delete")) {
            String newFileName = this.fileName + ".delete";
            try {
                Path newFilePath = Paths.get(newFileName);
                if (NetworkUtil.isWindowsPlatform() && mappedByteBuffer != null) {
                    long position = this.fileChannel.position();
                    UtilAll.cleanBuffer(this.mappedByteBuffer);
                    this.fileChannel.close();
                    Files.move(Paths.get(fileName), newFilePath, StandardCopyOption.ATOMIC_MOVE);
                    try (RandomAccessFile file = new RandomAccessFile(newFileName, "rw")) {
                        this.fileChannel = file.getChannel();
                        this.fileChannel.position(position);
                        this.mappedByteBuffer = this.fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);
                    }
                }
                else {
                    Files.move(Paths.get(fileName), newFilePath, StandardCopyOption.ATOMIC_MOVE);
                }
                this.fileName = newFileName;
                this.file = new File(newFileName);
            } catch (IOException e) {
                log.error("move file {} failed", fileName, e);
            }
        }
    }



    @Override
    public void moveToParent() throws IOException {
        Path currentPath = Paths.get(fileName);
        String baseName = currentPath.getFileName().toString();
        Path parentPath = currentPath.getParent().getParent().resolve(baseName);
        if (NetworkUtil.isWindowsPlatform() && mappedByteBuffer != null) {
            long position = this.fileChannel.position();
            UtilAll.cleanBuffer(this.mappedByteBuffer);
            this.fileChannel.close();
            Files.move(Paths.get(fileName), parentPath, StandardCopyOption.ATOMIC_MOVE);
            try (RandomAccessFile file = new RandomAccessFile(parentPath.toFile(), "rw")) {
                this.fileChannel = file.getChannel();
                this.fileChannel.position(position);
                this.mappedByteBuffer = this.fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);
            }
        } else {
            Files.move(Paths.get(fileName), parentPath, StandardCopyOption.ATOMIC_MOVE);
        }
        this.file = parentPath.toFile();
        this.fileName = parentPath.toString();
    }


    @Override
    public String toString() {
        return this.fileName;
    }


    public long getStartTimestamp() {
        return startTimestamp;
    }


    public void setStartTimestamp(long startTimestamp) {
        this.startTimestamp = startTimestamp;
    }


    public long getStopTimestamp() {
        return stopTimestamp;
    }


    public void setStopTimestamp(long stopTimestamp) {
        this.stopTimestamp = stopTimestamp;
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：创建从指定位置开始的消息缓冲区迭代器
     */
    public Iterator<SelectMappedBufferResult> iterator(int startPos) {
        return new Itr(startPos);
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：得到底层unsafe对象的方法
     */
    public static Unsafe getUnsafe() {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            return (Unsafe) f.get(null);
        } catch (Exception ignore) {

        }
        return null;
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：将指定地址对齐缓存页的方法
     */
    public static long mappingAddr(long addr) {
        //计算指定地址相对于缓存页大小的偏移量，如果addr为5000，则offset为5000 % 4096 = 904
        long offset = addr % UNSAFE_PAGE_SIZE;
        //处理offset为负数的情况，这种情况并不会出现
        offset = (offset >= 0) ? offset : (UNSAFE_PAGE_SIZE + offset);
        //返回对齐后的内存地址
        //5000-904=4096
        return addr - offset;
    }

    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：计算指定内存大小占用的缓存页的数量
     */
    public static int pageCount(long size) {
        return (int) (size + (long) UNSAFE_PAGE_SIZE - 1L) / UNSAFE_PAGE_SIZE;
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：判断指定内存区域的数据是否加载了
     */
    @Override
    public boolean isLoaded(long position, int size) {
        if (IS_LOADED_METHOD == null) {
            return true;
        }
        try {
            long addr = ((DirectBuffer) mappedByteBuffer).address() + position;
            return (boolean) IS_LOADED_METHOD.invoke(
                    mappedByteBuffer,
                    mappingAddr(addr),
                    size,
                    pageCount(size)
            );
        } catch (Exception e) {
            log.info("invoke isLoaded0 of file {} error:", file.getAbsolutePath(), e);
        }
        return true;
    }


    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @方法描述：内存映射文件消息迭代器
     */
    private class Itr implements Iterator<SelectMappedBufferResult> {
        private int start;
        private int current;
        private ByteBuffer buf;


        public Itr(int pos) {
            this.start = pos;
            this.current = pos;
            this.buf = mappedByteBuffer.slice();
            this.buf.position(start);
        }


        @Override
        public boolean hasNext() {
            return current < getReadPosition();
        }


        @Override
        public SelectMappedBufferResult next() {
            int readPosition = getReadPosition();
            if (current < readPosition && current >= 0) {
                if (hold()) {
                    ByteBuffer byteBuffer = buf.slice();
                    byteBuffer.position(current);
                    int size = byteBuffer.getInt(current);
                    ByteBuffer bufferResult = byteBuffer.slice();
                    bufferResult.limit(size);
                    current += size;
                    return new SelectMappedBufferResult(
                            fileFromOffset + current,
                            bufferResult,
                            size,
                            DefaultMappedFile.this
                    );
                }
            }
            return null;
        }


        @Override
        public void forEachRemaining(Consumer<? super SelectMappedBufferResult> action) {
            Iterator.super.forEachRemaining(action);
        }


        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

}
